#! /usr/bin/python
from chimera.core.cli import ChimeraCLI, action, ParameterType
from chimera.util.coord import Coord
import sys
import os
import os.path

class DatabaseCLI(ChimeraCLI):
    def __init__(self):
        ChimeraCLI.__init__(self, "chimera-db", "Database controller", 0.1)
        self.addHelpGroup("Get", "Specify content to add or query")
        self.addController(name="db", cls="Database", required=True, helpGroup="Get", help="Database to be accessed")
        self.addParameters(dict(name="ra", short="r", long="ra", type="string", default=None, helpGroup="Get", help="Specifies RA of a star to add or query"),
                           dict(name="dec", short="d", long="dec", type="string", default=None, helpGroup="Get", help="Specifies Dec of a star to add or query"),
                           dict(name="star", short="s", long="star", type="string", default=None, helpGroup="Get", help="Specifies name of a star to add or query"),
                           dict(name="exp", short="e", long="exposure", type="string", default=None, helpGroup="Get", help="Specifies filename of an exposure to add or query"),
                           dict(name="note", short="n", long="note", type="string", default=None, helpGroup="Get", help="Specifies name of an exposure note to add"),
                           dict(name="cat", short="c", long="catalog", type="string", default=None, helpGroup="Get", help="Specifies name of a catalog to add or query (defaults to field name)"))
        self.addHelpGroup("Add", "Add content")
        self.addParameters(dict(name="radius", long="radius", type="int", default=5, helpGroup="Add", help="Radius to include when adding a field from Simbad, in arcminutes. Note that the default value if specifying an exposure is the largest axis of the exposure."),
                           dict(name="types", long="types", type="string", default="*iC,~V*", helpGroup="Add", help="Comma-separated list of object types to include or exclude (start with ~) when adding a field from Simbad"))
        self.addHelpGroup("Query", "Query content")
        self.addParameters(dict(name="exps", long="exposures", type=ParameterType.BOOLEAN, default=False, helpGroup="Query", help="Queries a list of all exposures in the database"),
                           dict(name="stars", long="stars", type=ParameterType.BOOLEAN, default=False, helpGroup="Query", help="Queries a list of all stars in the database"),
                           dict(name="cats", long="catalogs", type=ParameterType.BOOLEAN, default=False, helpGroup="Query", help="Queries a list of all catalogs in the database"),
                           dict(name="hasexps", long="has-exposures", type=ParameterType.BOOLEAN, default=False, helpGroup="Query", help="Filters a list of stars to remove those without any exposures"),
                           dict(name="file", long="file", short="f", type="string", helpGroup="Add", help="Gives a file location for data files to be added."),
                           dict(name="date", long="date-time", short = "t", type ="string", default = None, helpGroup="Query", help= "Restricts queries and deletions to within a certain range of time, defaulting to 12 hours."),
                           dict(name="time", long="time-range", short = "z", type="float", default=12, helpGroup = "Query", help= "Specifies the deviation from a specified date time in which to search, in hours."),
			   dict(name="hasmultiExps", long="has-multiple-exposures", type=ParameterType.BOOLEAN, default=False, helpGroup="Query", help="Filters a list of stars to remove those with less than two exposures"),
			   dict(name="nomultiexps", long="has-few-exposures", type=ParameterType.BOOLEAN, default=False, helpGroup="Query", help="Filters a list of stars to show those with too few exposures to calculate extinction values."),
                           dict(name="count", short="N", long="count", type=ParameterType.BOOLEAN, default=False, helpGroup="Query", help="Changes a query to only display a count and not a list"))
        self.addHelpGroup("Remove", "Remove content")
        self.addHelpGroup("Backup", "Backup and restore")
        self.addHelpGroup("View", "Advanced data views")
        self.addParameters(dict(name="params", long="params", type="string", default="", helpGroup="View", help="List of parameters to be sent to the viewing script"))
        self.count = False

    @action(short="A", helpGroup="Add", help="Add specified content to the database.  Please reference the help file distributed with this file for a full list of possible commands.")
    def add(self, options):
        if options.star:
            if ((options.ra and options.dec) and not options.cat):
                print self.db.addStarRADec(options.ra, options.dec, objname = options.star)
            if (options.cat and not (options.ra and options.dec)):
                self.db.addStarToCatalog(options.star, options.cat)
            if (options.cat and (options.ra and options.dec)):
                print self.db.addStarRADec(options.ra, options.dec, options.star, options.cat)
            else:
            	print "Unable to process input. Please specify an RA and Dec of the star or a catalog to which you wish the star moved."
        elif options.cat:
            if options.ra and options.dec:
            	star = (options.ra, options.dec)
                self.db.addStarToCatalog(star, options.cat)
            if options.stars and options.file:
            	stars = self.starsfromtext(options.file)
                self.db.importCatalog(options.cat, stars)
            elif options.stars and not options.file:
            	print "You must specify a file of RAs and Decs you wish to add to use the --stars command!"         	
            else:
                self.db.importCatalog(options.cat)
        elif options.ra and options.dec:
            self.db.addStarRADec(options.ra, options.dec)
        elif options.exp:
            if options.note:
                self.db.addExposureNote(options.exp, options.note)            	
            else:
                self.db.addExposure(options.exp)
        elif options.exps:
            	if options.note:
            		if options.date:
            			self.db.addExposureNote(options.exp, options.note, date=options.date, multi = True, time = options.time)
            		else:
            			self.db.addExposureNote(options.exp, options.note, multi = True)
	else:
	    print "Options were not parsable. Please check your spelling or consult the help dialogue."

    @action(short="Q", helpGroup="Query", help="Query specified content from the database. Please reference the help file distributed with this file for a full list of possible commands.")
    def query(self, options):
        self.count = options.count
        
        if options.ra and options.dec:
            if options.exp:
                self.printExposureStar((options.ra, options.dec), options.exp)
            else:
                self.printStar((options.ra, options.dec))
        elif options.star:
            if options.exp:
                self.printExposureStar(options.exp, options.star)
            else:
                self.printStar(options.star)
        elif options.exp:
            self.printExposure(options.exp)
        elif options.cat:
	    if options.hasmultiExps: multiexp = True 
	    elif options.nomultiexps: multiexp = False
	    else: multiexp = None
            self.printCatalog(options.cat, hasExps = options.hasexps, hasmultiexps=multiexp)
        elif options.exps and options.stars:
            self.printExposureStars()
        elif options.exps:
            self.printExposures()
        elif options.stars:
	    if options.hasmultiExps: multiexp = True 
	    elif options.nomultiexps: multiexp = False
	    else: multiexp = None
            self.printStars(hasExps = options.hasexps, hasmultiexps=multiexp)
        elif options.cats:
            self.printCatalogs()
	else:
		print "Your query could not be parsed. Please check your spelling or refer to the help dialog."

    @action(short="D", helpGroup="Remove", help="Remove specified content from the database. Please reference the help file distributed with this file for a full list of possible commands.")
    def remove(self, options):
        if options.cat:
            if options.ra and options.dec:
                self.db.removeCatalogStar((options.ra, options.dec), options.cat)
            elif options.star:
                self.db.removeCatalogStar(options.star, options.cat)
            else:
		movetoAuto = self.wanttomove()
                self.db.removeCatalog(options.cat, movetoAuto)
        elif options.exp:
            if options.note:
                self.db.removeExposureNote(options.exp, options.note)
            elif options.ra and options.dec:
                self.db.removeExposureStar(options.exp, (options.ra, options.dec))
            elif options.star: 
                self.db.removeExposureStar(options.exp, options.star)


    @action(short="B", type="string", helpGroup="Backup", help="Copies all the contents of the database to a separate file")
    def backup(self, options):
        self.db.backup(options.backup)

    @action(short="R", type="string", helpGroup="Backup", help="Imports database contents from a backup file")
    def restore(self, options):
        self.db.restore(options.restore)

    @action(short="V", type="string", helpGroup="View", help="Runs a custom script to generate a table or plot of the data in the database")
    def view(self, options):
        if options.params == "": params = {}
        else: params = dict([tuple(p.split(':', 2)) for p in options.params.split(',')])
        
        dv = self.db.getViewer()
        try:
            files = dv.runScript(options.view, params)
            if len(files) > 0:
                print len(files), "file(s) created:"
                for file in files: print "  ", file
        except Exception, e:
            print e

    @action(long="simbad", short="S", helpGroup="Add", help="Adds stars from a field from the Simbad catalog")
    def simbad(self, options):
    	stars = []

        if options.star:
            stars.append(options.star)
	elif options.exp:
	    exp = self.db.getExposure(options.exp)
	    expstars=self.db.getExposureStars(options.exp)
	    stars = []
	    for expstar in expstars:
	    	stars.append(expstar.getStar())
        elif options.ra and options.dec:
            stars.append(Star(options.ra, options.dec))
        elif options.cat:
            	stars = self.db.getStars(catalog = options.cat)
        #############################
        #
        # NEED SUPPORT FOR STARS, EXPS, multi/few switches, datetime
        #
        #############################
        else:
            return
            
	

        if options.cat:

            if options.cat.find(',') >= 0: catalog = options.cat.split(',')
            else:                          catalog = options.cat
        elif options.star:                 catalog = options.star
	if options.cat == None: catalog = self.db.getAutoCatalogName()
	
	for star in stars:
        	if options.radius: 
            		if options.types:
                		self.db.importSimbadField(star, options.radius, catalog=catalog, objTypeFilter=options.types.split(','))
            		else:
                		self.db.importSimbadField(star, options.radius, catalog=catalog)
        	else:
            		if options.types:
                		self.db.importSimbadField(star, catalog=catalog, objTypeFilter=options.types.split(','))
            		else:
                		self.db.importSimbadField(star, catalog=catalog)
	print "\nImport from Simbad has ended."

    def printStar(self, id):
        stars = self.db.getStars(name = id)
        if len(stars) !=0:
        	print stars
        	print id
        	for star in stars:
            		print
            		print star.objname, "(", Coord.fromD(star.ra).toHMS(), ",", Coord.fromD(star.dec).toDMS(), ")"
            		print "Magnitudes:", len(star.magnitudes)
            		if not self.count:
                		for (filter, mag) in star.magnitudes.items():
                    			print "  ", filter, mag
            		print "Catalogs:", len(star.catalogs)
            		if not self.count:
                		for cat in star.catalogs:
                    			print "  ", cat
            		print "Exposures:", len(star.exposures)
            		if not self.count:
                		for exp in star.exposures:
                    			print "  ", exp
            			print
        else:
            print "Star not found"

    def printStars(self, hasExps = False, hasmultiexps = None):
        print
        if hasExps:
            stars = self.db.getStars(hasExposures = True)
	elif (hasmultiexps != None):
	    stars = self.db.getStars(hasMultiExposures = hasmultiexps)
        else:
            stars = self.db.getStars()
        print "Stars:", len(stars)
        if not self.count:
            for star in stars:
                print "  ", star.objname, "(", Coord.fromD(star.ra).toHMS(), ",", Coord.fromD(star.dec).toDMS(), ")"
        print

    def printExposure(self, id):
        exp = self.db.getExposure(id)
        if exp:
            print
            print exp.filename
            print exp.obsdate, ",", exp.imagetype, ",", exp.exptime, "sec ," , exp.filter, "filter"
            print "Center RA/Dec:", Coord.fromD(exp.ra).toHMS(), ",", Coord.fromD(exp.dec).toDMS()
            print "Center Alt/Az:", Coord.fromD(exp.alt).toDMS(), ",", Coord.fromD(exp.az).toDMS()
            print "Observer:", exp.observer
            print "Site:", Coord.fromD(exp.latitude).toDMS(), "lat", Coord.fromD(exp.longitude).toDMS(), "long"
            print "Local sidereal time:", Coord.fromD(exp.lst).toHMS()
            print "Notes:", len(exp.notes)
            if not self.count:
                for note in exp.notes:
                    print "  ", note.name, "(", note.date, ")", note.detail
            print "Stars found in image:", len(exp.stars)
            if not self.count:
                for star in exp.stars:
                    print "  ", star.star.objname,
                    print "(", Coord.fromD(star.star.ra).toHMS(), ",", Coord.fromD(star.star.dec).toDMS(), ")",
                    print "(Detected ", Coord.fromD(star.photra).toHMS(), ",", Coord.fromD(star.photdec).toDMS(), ")"
            print
        else:
            print "Exposure not found"

    def printExposures(self):
        print
        exps = self.db.getExposures()
        print "Exposures:", self.db.getExposureCount()
        if not self.count:
            for exp in exps:
                print "  ", exp.filename
        print

    def printExposureStar(self, exposure, star):
        expstar = self.db.getExposureStar(exposure, star)
        if expstar:
            print
            print expstar.exposure.filename
            print expstar.exposure.obsdate, ",", expstar.exposure.imagetype, ",", expstar.exposure.exptime, "sec ," , expstar.exposure.filter, "filter"
            print "Observer:", expstar.exposure.observer
            print "Site:", Coord.fromD(expstar.exposure.latitude).toDMS(), "lat", Coord.fromD(expstar.exposure.longitude).toDMS(), "long"
            print "Local sidereal time:", Coord.fromD(expstar.exposure.lst).toHMS()
            print "  ", expstar.star.objname, "(", Coord.fromD(expstar.star.ra).toHMS(), ",", Coord.fromD(expstar.star.dec).toDMS(), ")"
            print "Detected RA/Dec: (", Coord.fromD(expstar.photra).toHMS(), ",", Coord.fromD(expstar.photdec).toDMS(), ")"
            print "Alt/Az: (", Coord.fromD(expstar.alt).toDMS(), ",", Coord.fromD(expstar.az).toDMS(), ")"
            print "FWHM:", expstar.fwhm
            print "Flux:", expstar.flux
            print "Magnitude:", expstar.magnitude
            print "Airmass:", expstar.airmass
            print
        else:
            print "Exposure star not found"

    def printExposureStars(self):
        print
        exps = self.db.getExposures()
        print "Exposure Stars:", self.db.getExposureStarCount()
        if not self.count:
            for exp in exps:
                print "  ", exp.filename
                for star in exp.stars:
                    print "    ", star.star.objname,
                    print "(", Coord.fromD(star.star.ra).toHMS(), ",", Coord.fromD(star.star.dec).toDMS(), ")",
                    print "(Detected ", Coord.fromD(star.photra).toHMS(), ",", Coord.fromD(star.photdec).toDMS(), ")"
        print

    def printCatalog(self, id, hasExps = False, hasmultiexps= None ):
        print
        if hasExps: stars = self.db.getStars(catalog = id, hasExposures = True)
	elif (hasmultiexps != None): stars = self.db.getStars(catalog = id, hasMultiExposures = hasmultiexps)
        else:       stars = self.db.getStars(catalog = id)

        print id
       	print "Stars in catalog:", len(stars)
        if not self.count:
            	for star in stars:
                	print "  ", star.objname, "(", Coord.fromD(star.ra).toHMS(), ",", Coord.fromD(star.dec).toDMS(), ")"
        print

    def printCatalogs(self):
        print
        cats = self.db.getCatalogNames()
        print "Catalogs:", len(cats)
        if not self.count:
            for cat in cats:
                stars = self.db.getStars(catalog = cat)
                print "  ", cat, len(stars), "star(s)"
        print

    def wanttomove(self):
	truthiness = None
	while True:
		truthiness=str(raw_input('Do you want to move all of the stars from this catalog to the automatic catalog? [Y/N]: '))

		if (truthiness not in ['Y','N','y','n','yes','no','Yes','No']):
			print 'Please enter y or n.'
		if (truthiness in ['Y','y','yes','Yes']):
			truthiness = True
			break
		else:
			truthiness = False
			break
	return truthiness
    
    def starsfromtext(self, starFile):
	if os.path.isfile(starFile):
		f = open(starFile)
		line = f.readline()
		ra = []
		dec = []
		name = []
		while line:
			word = line.split(' ')
			ra.append(float(word[0]))
			dec.append(float(word[1]))
			try:
				name.append(string(word[2]))
			except:
				name.append(None)
			line=f.readline()
		stars = []
		for n in range(len(ra)):
			try:
			    star = self.database.getStar([ra[n], dec[n]])
			except:
			    if name[n] != None:
			    	star = self.database.addStarRADec(ra[n], dec[n], objname = name[n])
			    else: star = self.database.addStarRADec(ra[n], dec[n])
			stars.append(star)
		return stars
def main():
    cli = DatabaseCLI()
    cli.run(sys.argv)
    cli.wait()

if __name__ == '__main__':
    main()
